PHP实现linux命令tail -f
今天突然想到之前有人问过我的一个问题，如何通过PHP实现linux中的命令 tail -f ，这里就来分析实现下。

这个想一想也挺简单，通过一个循环检测文件，看文件的大小是否有变化，如果有变化，输出文件变化的部分，当然了这里面会有好多的细节，这里具体分析下。

如果初始文件太大或者改变内容太多
这个时候一下输出好多内容可能看不清，因此我这里设置了一个阈值 8192 ，当内容长度超过这个阈值的时候，只输出最后面的 8192 个字节，这样就不会出现大面积的刷新导致看不清的问题。

如何检测文件大小的变化
这个问题是这个程序的核心，能不能成功，性能的好坏就靠这部分了。我在这里的实现是下面这样：

打开文件句柄 $fp ，这里要注意，这里的文件句柄全程需中只打开一次关闭一次，因此要将他放在循环的外面。
初始化当前文件大小 file_size 和 file_size_new 都为0。
循环里面更新 file_size_new 文件大小，这里要注意，php中获取文件大小之前一定要运行函数 clearstatcache() ，清除文件状态缓存，否则获取文件大小可能会有偏差。
计算 add_size = file_size_new - file_size ，看文件大小是否有变化，如果有变化，将文件指针移动到指定位置，然后输出新加的内容，更新 file_size 值为 new_file_size 。
usleep(50000)，睡眠1/20秒。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env php
<?php
if(2 != count($argv)){
    fwrite(
        STDERR,
        "调用格式错误！使用格式 ./tail filename".PHP_EOL
    );
    return 1;
}

$file_name      = $argv[1];
define("MAX_SHOW", 8192);

$file_size      = 0;
$file_size_new  = 0;
$add_size       = 0;
$ignore_size    = 0;
$fp = fopen($file_name, "r");
while(1){
    clearstatcache();
    $file_size_new  = filesize($file_name);
    $add_size       = $file_size_new - $file_size;
    if($add_size > 0){
        if($add_size > MAX_SHOW){
            $ignore_size    = $add_size - MAX_SHOW;
            $add_size       = MAX_SHOW;
            fseek($fp, $file_size + $ignore_size);
        }
        fwrite(
            STDOUT,
            fread($fp, $add_size)
        );
        $file_size  = $file_size_new;
    }
    usleep(50000);
}

fclose($fp);
//代码实现这里第一行的 #!/usr/bin/env php 是告诉可执行文件，可执行文件 php 在系统 PATH 中查找，这样的好处就是移植性好。

//2016-02-22 11:28:51改进

//查了PHP官方手册， fseek 函数这里可以改进改进，这个函数还接受第三个参数，表示偏移指针的类型，默认是 SEEK_SET ，从开始偏移，还可以设置为 SEEK_CUR ，表示从当前位置偏移，因此这里改为 fseek($fp, $ignore_size, $ignore_size);